package com.renfei.mineplayer;


import android.annotation.SuppressLint;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.text.TextUtils;
import android.util.Log;

import com.renfei.mineplayer.listener.MineOnErrorListener;
import com.renfei.mineplayer.listener.MineOnLoadListener;
import com.renfei.mineplayer.listener.MineOnParparedListener;
import com.renfei.mineplayer.listener.MineOnTimeInfoListener;
import com.renfei.mineplayer.listener.MinePauseResumeListener;
import com.renfei.mineplayer.listener.MineValumeDBListener;
import com.renfei.mineplayer.listener.OnCompleteListener;
import com.renfei.mineplayer.listener.OnPcmDataCallBackListener;
import com.renfei.mineplayer.listener.OnRecordTimeListener;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class MinePlayer {

    private static final String TAG = "MinePlayer";

    static {
        System.loadLibrary("native-lib");
    }


    private String source;
    private int duration = -1;
    private static boolean playNext = false;
    private static int volume = 50;
    private static MuteEnum currentMute = MuteEnum.MUTE_CENTER;
    private static float currentPitch = 1.0f;
    private static float currentSpeed = 1.0f;


    private MineOnParparedListener mineOnParparedListener;
    private MineOnLoadListener mineOnLoadListener;
    private MinePauseResumeListener minePauseResumeListener;
    private MineOnTimeInfoListener mineOnTimeInfoListener;
    private static MineTimeInfo mineTimeInfo;
    private MineOnErrorListener mineOnErrorListener;
    private OnCompleteListener onCompleteListener;
    private MineValumeDBListener mineValumeDBListener;
    private OnRecordTimeListener onRecordTimeListener;
    private OnPcmDataCallBackListener onPcmDataCallBackListener;

    public MinePlayer() {

    }

    public void setSource(String source) {
        this.source = source;
    }

    public void setMineOnParparedListener(MineOnParparedListener mineOnParparedListener) {
        this.mineOnParparedListener = mineOnParparedListener;
    }

    public void setMineOnLoadListener(MineOnLoadListener mineOnLoadListener) {
        this.mineOnLoadListener = mineOnLoadListener;
    }

    public void setMinePauseResumeListener(MinePauseResumeListener minePauseResumeListener) {
        this.minePauseResumeListener = minePauseResumeListener;
    }

    public void setMineOnTimeInfoListener(MineOnTimeInfoListener mineOnTimeInfoListener) {
        this.mineOnTimeInfoListener = mineOnTimeInfoListener;
    }

    public void setMineOnErrorListener(MineOnErrorListener mineOnErrorListener) {
        this.mineOnErrorListener = mineOnErrorListener;
    }

    public void setOnCompleteListener(OnCompleteListener onCompleteListener) {
        this.onCompleteListener = onCompleteListener;
    }

    public void setMineValumeDBListener(MineValumeDBListener mineValumeDBListener) {
        this.mineValumeDBListener = mineValumeDBListener;
    }

    public void setOnRecordTimeListener(OnRecordTimeListener onRecordTimeListener) {
        this.onRecordTimeListener = onRecordTimeListener;
    }

    public void setOnPcmDataCallBackListener(OnPcmDataCallBackListener onPcmDataCallBackListener) {
        this.onPcmDataCallBackListener = onPcmDataCallBackListener;
    }

    public int getDuration() {
        if (duration < 0) {
            duration = n_duration();
        }
        return duration;
    }

    public void parpared() {
        if (TextUtils.isEmpty(source)) {
            MineLog.e("music source is empty");
            return;
        }

        onCallLoad(true);
        /**
         * 在子线程中开启 准备操作
         */
        new Thread(new Runnable() {
            @Override
            public void run() {
                n_parpared(source);
            }
        }).start();
    }

    public void startRecord(File outFile) {
        if (!isInitMediacodec) {
            audioSamplerRate = n_samplerate();
            if (n_samplerate() > 0) {
                initMediaCodec(audioSamplerRate, outFile);
                isInitMediacodec = true;
                n_startStopRecord(true);
                MineLog.e("开始录制");
            }
        }
    }

    public void stopRecord() {
        if (isInitMediacodec) {
            n_startStopRecord(false);
            realeaseMediaCodec();
            MineLog.e("结束录制");
        }
    }


    public void resumeRecord() {
        n_startStopRecord(true);
        MineLog.e("继续录制");
    }

    public void pauseRecord() {
        n_startStopRecord(false);
        MineLog.e("暂停录制");
    }

    public void cutAudioPlay(int start_time, int end_time, boolean showPcm) {
        if (n_cutaudioplay(start_time, end_time, showPcm)) {
            start();
        } else {
            stop();
            onCallError(2001, "cut audio params is error");
        }
    }

    /**
     * C++调用Java的方法    start
     **/
    public void onCallParpared() {
        if (null != mineOnParparedListener) {
            mineOnParparedListener.onParpared();
        }
    }

    public void onCallLoad(boolean load) {
        if (null != mineOnLoadListener) {
            mineOnLoadListener.onLoad(load);
        }
    }

    /*当前播放时间 与总时长*/
    public void onCallTimeInfo(int currentTime, int totalTime) {
        if (null != mineOnTimeInfoListener) {
            if (null == mineTimeInfo) {
                mineTimeInfo = new MineTimeInfo();
            }
            mineTimeInfo.setCurrentTime(currentTime);
            mineTimeInfo.setTotalTime(totalTime);
            mineOnTimeInfoListener.onTimeInfo(mineTimeInfo);
        }
    }

    public void onCallError(int code, String msg) {
        if (mineOnErrorListener != null) {
            mineOnErrorListener.onError(code, msg);
        }
    }


    public void onCallComplete() {
        stop();
        if (onCompleteListener != null) {
            onCompleteListener.onComplete();
        }
    }

    public void onCallNext() {
        if (playNext) {
            playNext = false;
            parpared();
        }
    }

    public void onCCallValumeDB(int db) {
        if (null != mineValumeDBListener) {
            mineValumeDBListener.onDbValue(db);
        }
    }

    public void onCallPcmData(byte[] buffer, int bufferSize) {

//        Log.e(TAG, "onCallPcmData bufferSize: "+bufferSize);
        if (null != onPcmDataCallBackListener) {
            onPcmDataCallBackListener.onPcmCallBack(buffer, bufferSize);
        }
    }

    ;

    public void onCallPcmRate(int pcmRate) {
        if (null != onPcmDataCallBackListener) {
            onPcmDataCallBackListener.onPcmRate(pcmRate, 16, 2);
        }
    }

    ;

    public void onCallRenderYuv(int width, int height, byte[] y, byte[] u, byte[] v){

        MineLog.e("onCallRenderYuv width =" +width +"height = "+height);
    }

    /**
     * C++调用Java的方法    end
     **/

    public void start() {
        if (TextUtils.isEmpty(source)) {
            MineLog.e("source is empty");
            return;
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                setVolume(volume);
                setMute(currentMute);
                setPtitch(currentPitch);
                setSpeed(currentSpeed);
                n_start();
            }
        }).start();
    }


    public void pause() {
        n_pause();
        pauseRecord();
        if (null != minePauseResumeListener) {
            minePauseResumeListener.onPause(true);
        }
    }

    public void resume() {
        n_resume();
        resumeRecord();
        if (null != minePauseResumeListener) {
            minePauseResumeListener.onPause(false);
        }
    }

    public void stop() {
        mineTimeInfo = null;
        duration = -1;
        stopRecord();
        new Thread(new Runnable() {
            @Override
            public void run() {
                n_stop();
            }
        }).start();
    }

    public void onSeek(int seconds) {
        n_seek(seconds);
    }

    public void playNext(String url) {
        source = url;
        playNext = true;
        stop();
    }

    public void setVolume(int percent) {
        if (percent >= 0 && percent <= 100) {
            volume = percent;
            setMute(currentMute);
            n_volume(percent);
        }
    }

    public void setMute(MuteEnum mute) {
        currentMute = mute;
        n_mute(mute.getValue());
    }

    public void setPtitch(float pitch) {
        currentPitch = pitch;
        n_pitch(pitch);
    }

    public void setSpeed(float speed) {
        currentSpeed = speed;
        n_speed(speed);
    }

    public int getVolume() {
        return volume;
    }

    private native void n_parpared(String source);

    private native void n_start();

    private native void n_pause();

    private native void n_resume();

    private native void n_stop();

    private native void n_seek(int seek);

    private native int n_duration();


    private native void n_volume(int persent);

    private native void n_mute(int mute);

    private native void n_pitch(float pitch);

    private native void n_speed(float speed);

    private native int n_samplerate();

    private native void n_startStopRecord(boolean start);

    private native boolean n_cutaudioplay(int starTime, int endTime, boolean showPcm);

    //mediaCodec
    private MediaFormat encodecFormat = null;
    private MediaCodec mediaCodec = null;

    private FileOutputStream fileOutputStream = null;
    private MediaCodec.BufferInfo bufferInfo = null;

    private int perpcmSize = 0;
    private byte[] outByteBuffer = null;
    private int aacSampleRate = 4;

    private boolean isInitMediacodec = false;
    private double recordTime = 0;
    private int audioSamplerRate = 0;

    @SuppressLint("WrongConstant")
    private void initMediaCodec(int sampleRate, File outputFile) {
        try {
            aacSampleRate = getADTSsamplerate(sampleRate);


            encodecFormat = MediaFormat.createAudioFormat(
                    MediaFormat.MIMETYPE_AUDIO_AAC,
                    sampleRate, 2
            );
            //设置码率
            encodecFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);
            encodecFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            encodecFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 4096);
            mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
            bufferInfo = new MediaCodec.BufferInfo();
            if (null == mediaCodec) {
                MineLog.e("create mediaCodec error");
            }
            recordTime = 0;

            mediaCodec.configure(encodecFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            fileOutputStream = new FileOutputStream(outputFile);
            mediaCodec.start();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //处理mediaCodec 输入缓冲
    private void encodecPcmToAAc(int size, byte[] buffer) {
        MineLog.d("buffer size is : " + size);
        if (null != buffer && null != mediaCodec) {

            recordTime += size * 1.0 / (audioSamplerRate * 2 * (16 / 8));
            if (null != onRecordTimeListener) {
                onRecordTimeListener.onRecordTime((int) recordTime);
            }
            int inputBufferIndex = mediaCodec.dequeueInputBuffer(0);
            if (inputBufferIndex >= 0) {
                ByteBuffer byteBuffer = null;
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                    byteBuffer = mediaCodec.getInputBuffer(inputBufferIndex);
                }
                byteBuffer.clear();
                byteBuffer.put(buffer);
                mediaCodec.queueInputBuffer(inputBufferIndex, 0, size, 0, 0);
            }


            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);

            if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {

            } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

            } else {
                while (outputBufferIndex >= 0) {

                    try {
                        perpcmSize = bufferInfo.size + 7;
                        outByteBuffer = new byte[perpcmSize];

                        //取出缓冲区内的信息
                        ByteBuffer byteBuffer = null;
                        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                            byteBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);
                        }
                        byteBuffer.position(bufferInfo.offset);
                        byteBuffer.limit(bufferInfo.offset + bufferInfo.size);

                        //添加ADTS头部 并填充数据给 输出的 ByterBuffer
                        addADTStoPacket(outByteBuffer, perpcmSize, aacSampleRate);
                        byteBuffer.get(outByteBuffer, 7, bufferInfo.size);
                        byteBuffer.position(bufferInfo.offset);

                        //写入到文件
                        fileOutputStream.write(outByteBuffer, 0, perpcmSize);


                        mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                        outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);

                        //清空输出ByteBuffer
                        outByteBuffer = null;

//                        MineLog.e("编码中...");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

    }


    /**
     * 添加ADTS头
     *
     * @param packet
     * @param packetLen
     */
    private void addADTStoPacket(byte[] packet, int packetLen, int samplerate) {
        int profile = 2; // AAC LC
        int freqIdx = samplerate; // 44.1KHz
        int chanCfg = 2; // CPE

        // fill in ADTS data
        packet[0] = (byte) 0xFF;
        packet[1] = (byte) 0xF9;
        packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
        packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
        packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
        packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
        packet[6] = (byte) 0xFC;
    }


    private int getADTSsamplerate(int samplerate) {
        int rate = 4;
        switch (samplerate) {
            case 96000:
                rate = 0;
                break;
            case 88200:
                rate = 1;
                break;
            case 64000:
                rate = 2;
                break;
            case 48000:
                rate = 3;
                break;
            case 44100:
                rate = 4;
                break;
            case 32000:
                rate = 5;
                break;
            case 24000:
                rate = 6;
                break;
            case 22050:
                rate = 7;
                break;
            case 16000:
                rate = 8;
                break;
            case 12000:
                rate = 9;
                break;
            case 11025:
                rate = 10;
                break;
            case 8000:
                rate = 11;
                break;
            case 7350:
                rate = 12;
                break;
        }
        return rate;
    }

    private void realeaseMediaCodec() {
        if (null == mediaCodec) {
            return;
        }
        try {
            recordTime = 0;

            fileOutputStream.close();
            fileOutputStream = null;

            mediaCodec.stop();
            mediaCodec.release();
            mediaCodec = null;

            encodecFormat = null;
            bufferInfo = null;

            isInitMediacodec = false;

            MineLog.e("录制完成");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != fileOutputStream) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                fileOutputStream = null;
            }
        }

    }

}
